package com.piece.core.db.util.execute;

import com.piece.core.db.util.DataBaseUtil;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.support.convert.Convert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * sql执行器
 */
@Slf4j
public class SqlExecutor {

    private static final Map<String, PreparedStatement> statementMap = new ConcurrentHashMap<>();
    private static final ReentrantLock lock = new ReentrantLock();

    private static SqlExecutor instance;

    public static SqlExecutor getInstance() {
        if (null == instance) {
            lock.lock();
            if (null == instance) {
                instance = new SqlExecutor();
            }
            lock.unlock();
        }
        return instance;
    }

    /**
     * 取消执行
     */
    public boolean cancel(String executeId) {
        PreparedStatement preparedStatement = statementMap.remove(executeId);
        try {
            if (null != preparedStatement) {
                preparedStatement.cancel();
                return true;
            }
        } catch (Exception e) {
            log.error("取消执行SQL语句异常：{}", e.getMessage());
        }
        return false;
    }

    /**
     * 执行sql，返回结果
     */
    public Object[] executeResultSet(ExecuteParam executeParam, ExecuteResultHandler handler) {
        // 输出sql日志
        if (CollectionUtils.isEmpty(executeParam.getParamList())) {
            if (log.isDebugEnabled())
                log.info("执行sql ==> {}", executeParam.getSql());
        }

        PreparedStatement preparedStatement = null;
        Connection connection = null;
        try {
            DataBaseUtil.changeDataSource(executeParam.getDbKey());
            // 执行查询
            connection = DataBaseUtil.getDataSource().getConnection();
            String sql = executeParam.getSql();
            preparedStatement = connection.prepareStatement(sql);
            // 设置当前的PreparedStatement
            statementMap.put(executeParam.getExecuteId(), preparedStatement);
            List<Object> paramDataList = executeParam.getParamList();
            if (CollectionUtils.isNotEmpty(paramDataList)) {
                for (int i = 0; i < paramDataList.size(); i++) {
                    preparedStatement.setObject(i + 1, paramDataList.get(i));
                }
            }
            if (sql.toUpperCase().startsWith("SELECT")) {
                preparedStatement.executeQuery();
            } else {
                preparedStatement.execute();
            }
            // 限制下最大数量
            if (null != executeParam.getMaxRows()) {
                preparedStatement.setMaxRows(executeParam.getMaxRows());
            }
            // 查询的结果集
            ResultSet resultSet = preparedStatement.getResultSet();
            // 更新的数量，小于0代表不是更新语句
            int updateCount = preparedStatement.getUpdateCount();
            List<Map<String, Object>> resultList = new LinkedList<>();
            if (null != resultSet) {
                while (resultSet.next()) {
                    Map<String, Object> resultMap = new LinkedHashMap<>();
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    for (int i = 1; i < metaData.getColumnCount() + 1; i++) {
                        resultMap.put(metaData.getColumnName(i), resultSet.getObject(i));
                    }
                    if (null != handler) {
                        handler.handleResult(resultMap);
                    }
                    resultList.add(resultMap);
                }
            }

            return new Object[]{updateCount, resultList};
        } catch (Exception e) {
            log.error("执行SQL语句出错：{}", e.getMessage());
            throw new RuntimeException(e);
        } finally {
            statementMap.remove(executeParam.getExecuteId());
            try {
                if (null != preparedStatement && !preparedStatement.isClosed()) {
                    preparedStatement.close();
                }
            } catch (Exception e) {
                log.error("关闭Statement失败：{}", e.getMessage());
            }
            try {
                if (null != connection && !connection.isClosed()) {
                    connection.close();
                }
            } catch (Exception e) {
                log.error("回收connection失败：{}", e.getMessage());
            }
            DataBaseUtil.clearDataSource();
        }
    }

    /**
     * 执行sql，返回结果
     */
    public ExecuteResult execute(ExecuteParam param) {
        return this.execute(param, null);
    }

    /**
     * 执行sql，可通过handler回调每一行的结果
     */
    public ExecuteResult execute(ExecuteParam executeParam, ExecuteResultHandler handler) {
        // 执行查询
        try {
            long startTime = System.currentTimeMillis();
            Object[] resultObject = executeResultSet(executeParam, handler);
            int updateCount = Convert.toInt(resultObject[0], -1);
            List<Map<String, Object>> resultList = (List<Map<String, Object>>) resultObject[1];
            long useTime = System.currentTimeMillis() - startTime;
            return new ExecuteResult(updateCount, resultList, useTime, executeParam.getSql());
        } catch (Exception e) {
            log.error("解析ResultSet出错：{}", e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }
}
